import io
import os

import pdfkit
from datetime import datetime
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http.response import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render, reverse
from django.template.loader import render_to_string
from django.views.generic import (
    CreateView,
    DeleteView,
    DetailView,
    FormView,
    TemplateView,
    UpdateView,
    View,
)

from common.models import Address, Attachments, Comment, User
from common.tasks import send_email_user_mentions
from invoices.forms import (
    InvoiceAddressForm,
    InvoiceAttachmentForm,
    InvoiceCommentForm,
    InvoiceForm,
)
from invoices.models import Invoice
from invoices.tasks import (
    send_email,
    send_invoice_email,
    send_invoice_email_cancel,
    create_invoice_history,
)
from common.access_decorators_mixins import (
    sales_access_required,
    marketing_access_required,
    SalesAccessRequiredMixin,
    MarketingAccessRequiredMixin,
)
from teams.models import Teams


@login_required
def get_teams_and_users(request):
    data = {}
    teams = Teams.objects.all()
    teams_data = [
        {"team": team.id, "users": [user.id for user in team.users.all()]}
        for team in teams
    ]
    users = User.objects.all().values_list("id", flat=True)
    data["teams"] = teams_data
    data["users"] = list(users)
    return JsonResponse(data)


@login_required
@sales_access_required
def invoices_list(request):

    if request.user.role == "ADMIN" or request.user.is_superuser:
        users = User.objects.all()
    # elif request.user.google.all():
    #     # users = User.objects.none()
    #     # users = User.objects.filter(id=request.user.id)
    #     users = User.objects.filter(Q(role='ADMIN') | Q(id=request.user.id))
    elif request.user.role == "USER":
        users = User.objects.filter(Q(role="ADMIN") | Q(id=request.user.id))
    status = (
        ("Draft", "Draft"),
        ("Sent", "Sent"),
        ("Paid", "Paid"),
        ("Pending", "Pending"),
        ("Cancel", "Cancel"),
    )

    if request.method == "GET":
        context = {}
        if request.user.role == "ADMIN" or request.user.is_superuser:
            invoices = Invoice.objects.all().distinct().order_by("-created_on")
        else:
            invoices = (
                Invoice.objects.filter(
                    Q(created_by=request.user) | Q(assigned_to=request.user)
                )
                .distinct()
                .order_by("-created_on")
            )
        context["invoices"] = invoices.filter(company=request.company).order_by(
            "-created_on"
        )
        context["status"] = status
        context["users"] = users.filter(company=request.company)
        user_ids = list(invoices.values_list("created_by", flat=True))
        user_ids.append(request.user.id)
        context["created_by_users"] = users.filter(is_active=True, id__in=user_ids)
        today = datetime.today().date()
        context["today"] = today
        return render(request, "invoices_list.html", context)

    if request.method == "POST":
        context = {}
        context["status"] = status
        context["users"] = users.filter(company=request.company)
        invoices = Invoice.objects.filter(company=request.company).order_by(
            "-created_on"
        )
        if request.user.role == "ADMIN" or request.user.is_superuser:
            invoices = invoices
        else:
            invoices = invoices.filter(
                Q(created_by=request.user) | Q(assigned_to=request.user)
            ).distinct()

        if request.POST.get("invoice_title_number", None):
            invoices = invoices.filter(
                Q(invoice_title__icontains=request.POST.get("invoice_title_number"))
                | Q(invoice_number__icontains=request.POST.get("invoice_title_number"))
            )

        if request.POST.get("created_by", None):
            invoices = invoices.filter(created_by__id=request.POST.get("created_by"))

        if request.POST.getlist("assigned_to", None):
            invoices = invoices.filter(
                assigned_to__in=request.POST.getlist("assigned_to")
            )
            context["assigned_to"] = request.POST.getlist("assigned_to")

        if request.POST.get("status", None):
            invoices = invoices.filter(status=request.POST.get("status"))

        if request.POST.get("total_amount", None):
            invoices = invoices.filter(total_amount=request.POST.get("total_amount"))

        user_ids = list(invoices.values_list("created_by", flat=True))
        user_ids.append(request.user.id)
        context["created_by_users"] = users.filter(is_active=True, id__in=user_ids)
        context["invoices"] = invoices.distinct().order_by("-created_on")
        today = datetime.today().date()
        context["today"] = today
        return render(request, "invoices_list.html", context)


@login_required
@sales_access_required
def invoices_create(request):
    if request.method == "GET":
        context = {}
        context["form"] = InvoiceForm(request_user=request.user, request_obj=request)
        context["users"] = User.objects.filter(company=request.company)
        # "prefix" use case in form, both from address and to address use the
        # same model and same model form, so the name attribute in the form will
        #  be same for the both forms and the address will be same for both
        # shipping and to address, so use prefix parameter to distinguish the
        # forms, don't remove this comment
        context["from_address_form"] = InvoiceAddressForm(prefix="from")
        context["to_address_form"] = InvoiceAddressForm(prefix="to")
        if request.user.role == "ADMIN" or request.user.is_superuser:
            context["teams"] = Teams.objects.filter(company=request.company)

        return render(request, "invoice_create_1.html", context)

    if request.method == "POST":
        form = InvoiceForm(request.POST, request_user=request.user, request_obj=request)
        from_address_form = InvoiceAddressForm(request.POST, prefix="from")
        to_address_form = InvoiceAddressForm(request.POST, prefix="to")
        if (
            form.is_valid()
            and from_address_form.is_valid()
            and to_address_form.is_valid()
        ):
            from_address_obj = from_address_form.save()
            to_address_obj = to_address_form.save()
            invoice_obj = form.save(commit=False)
            invoice_obj.created_by = request.user
            invoice_obj.company = request.company
            invoice_obj.from_address = from_address_obj
            invoice_obj.to_address = to_address_obj
            invoice_obj.save()
            form.save_m2m()

            if request.POST.getlist("teams", []):
                user_ids = Teams.objects.filter(
                    id__in=request.POST.getlist("teams")
                ).values_list("users", flat=True)
                assinged_to_users_ids = invoice_obj.assigned_to.all().values_list(
                    "id", flat=True
                )
                for user_id in user_ids:
                    if user_id not in assinged_to_users_ids:
                        invoice_obj.assigned_to.add(user_id)

            if request.POST.getlist("teams", []):
                invoice_obj.teams.add(*request.POST.getlist("teams"))

            create_invoice_history(invoice_obj.id, request.user.id, [])
            kwargs = {"domain": request.get_host(), "protocol": request.scheme}
            assigned_to_list = list(
                invoice_obj.assigned_to.all().values_list("id", flat=True)
            )
            send_email.delay(invoice_obj.id, assigned_to_list, **kwargs)
            if request.POST.get("from_account"):
                return JsonResponse(
                    {
                        "error": False,
                        "success_url": reverse(
                            "accounts:view_account",
                            args=(request.POST.get("from_account"),),
                        ),
                    }
                )
            return JsonResponse(
                {"error": False, "success_url": reverse("invoices:invoices_list")}
            )
        else:
            return JsonResponse(
                {
                    "error": True,
                    "errors": form.errors,
                    "from_address_errors": from_address_form.errors,
                    "to_address_errors": to_address_form.errors,
                }
            )


@login_required
@sales_access_required
def invoice_details(request, invoice_id):
    invoice = get_object_or_404(
        Invoice.objects.select_related("from_address", "to_address"), pk=invoice_id
    )

    if invoice.company != request.company:
        raise PermissionDenied

    user_assigned_account = False
    user_assigned_accounts = set(
        request.user.account_assigned_users.values_list("id", flat=True)
    )
    invoice_accounts = set(invoice.accounts.values_list("id", flat=True))
    if user_assigned_accounts.intersection(invoice_accounts):
        user_assigned_account = True

    if not (
        (request.user.role == "ADMIN")
        or (request.user.is_superuser)
        or (invoice.created_by == request.user)
        or (request.user in invoice.assigned_to.all())
        or user_assigned_account
    ):
        raise PermissionDenied

    if request.method == "GET":
        context = {}
        context["invoice"] = invoice
        context["attachments"] = invoice.invoice_attachment.all()
        context["comments"] = invoice.invoice_comments.all()
        context["invoice_history"] = invoice.invoice_history.all()
        if request.user.is_superuser or request.user.role == "ADMIN":
            context["users_mention"] = list(
                User.objects.filter(is_active=True, company=request.company).values(
                    "username"
                )
            )
        elif request.user != invoice.created_by:
            context["users_mention"] = [{"username": invoice.created_by.username}]
        else:
            context["users_mention"] = list(
                invoice.assigned_to.all().values("username")
            )
        return render(request, "invoices_detail_1.html", context)


@login_required
@sales_access_required
def invoice_edit(request, invoice_id):
    invoice_obj = get_object_or_404(Invoice, pk=invoice_id)

    if invoice_obj.company != request.company:
        raise PermissionDenied

    if not (
        request.user.role == "ADMIN"
        or request.user.is_superuser
        or invoice_obj.created_by == request.user
        or request.user in invoice_obj.assigned_to.all()
    ):
        raise PermissionDenied

    if request.method == "GET":
        context = {}
        context["invoice_obj"] = invoice_obj
        context["teams"] = Teams.objects.filter(company=request.company)
        context["users"] = User.objects.filter(company=request.company)
        context["form"] = InvoiceForm(
            instance=invoice_obj, request_user=request.user, request_obj=request
        )
        context["from_address_form"] = InvoiceAddressForm(
            prefix="from", instance=invoice_obj.from_address
        )
        context["to_address_form"] = InvoiceAddressForm(
            prefix="to", instance=invoice_obj.to_address
        )
        return render(request, "invoice_create_1.html", context)

    if request.method == "POST":
        form = InvoiceForm(
            request.POST,
            instance=invoice_obj,
            request_user=request.user,
            request_obj=request,
        )
        from_address_form = InvoiceAddressForm(
            request.POST, instance=invoice_obj.from_address, prefix="from"
        )
        to_address_form = InvoiceAddressForm(
            request.POST, instance=invoice_obj.to_address, prefix="to"
        )
        previous_assigned_to_users = list(
            invoice_obj.assigned_to.all().values_list("id", flat=True)
        )
        if (
            form.is_valid()
            and from_address_form.is_valid()
            and to_address_form.is_valid()
        ):
            form_changed_data = form.changed_data
            form_changed_data.remove("from_address")
            form_changed_data.remove("to_address")
            form_changed_data = form_changed_data + [
                "from_" + field for field in from_address_form.changed_data
            ]
            form_changed_data = form_changed_data + [
                "to_" + field for field in to_address_form.changed_data
            ]
            # if form_changed_data:
            #     create_invoice_history(invoice_obj.id, request.user.id, form_changed_data)
            from_address_obj = from_address_form.save()
            to_address_obj = to_address_form.save()
            invoice_obj = form.save(commit=False)
            invoice_obj.created_by = request.user
            invoice_obj.from_address = from_address_obj
            invoice_obj.to_address = to_address_obj
            invoice_obj.save()
            form.save_m2m()
            if form_changed_data:
                create_invoice_history(
                    invoice_obj.id, request.user.id, form_changed_data
                )

            if request.POST.getlist("teams", []):
                user_ids = Teams.objects.filter(
                    id__in=request.POST.getlist("teams")
                ).values_list("users", flat=True)
                assinged_to_users_ids = invoice_obj.assigned_to.all().values_list(
                    "id", flat=True
                )
                for user_id in user_ids:
                    if user_id not in assinged_to_users_ids:
                        invoice_obj.assigned_to.add(user_id)

            if request.POST.getlist("teams", []):
                invoice_obj.teams.clear()
                invoice_obj.teams.add(*request.POST.getlist("teams"))
            else:
                invoice_obj.teams.clear()

            kwargs = {"domain": request.get_host(), "protocol": request.scheme}

            assigned_to_list = list(
                invoice_obj.assigned_to.all().values_list("id", flat=True)
            )
            recipients = list(set(assigned_to_list) - set(previous_assigned_to_users))
            # send_email_to_assigned_user.delay(recipients, account_object.id, domain=current_site.domain,
            #     protocol=self.request.scheme)

            send_email.delay(invoice_obj.id, recipients, **kwargs)
            if request.POST.get("from_account"):
                return JsonResponse(
                    {
                        "error": False,
                        "success_url": reverse(
                            "accounts:view_account",
                            args=(request.POST.get("from_account"),),
                        ),
                    }
                )
            return JsonResponse(
                {"error": False, "success_url": reverse("invoices:invoices_list")}
            )
        else:
            return JsonResponse(
                {
                    "error": True,
                    "errors": form.errors,
                    "from_address_errors": from_address_form.errors,
                    "to_address_errors": to_address_form.errors,
                }
            )


@login_required
@sales_access_required
def invoice_delete(request, invoice_id):
    invoice = get_object_or_404(Invoice, pk=invoice_id)

    if invoice.company != request.company:
        raise PermissionDenied

    if not (
        request.user.role == "ADMIN"
        or request.user.is_superuser
        or invoice.created_by == request.user
    ):
        raise PermissionDenied

    if request.method == "GET":
        invoice.delete()
        if request.GET.get("view_account"):
            return redirect(
                reverse(
                    "accounts:view_account", args=(request.GET.get("view_account"),)
                )
            )
        return redirect("invoices:invoices_list")


@login_required
@sales_access_required
def invoice_send_mail(request, invoice_id):
    invoice = get_object_or_404(Invoice, pk=invoice_id)
    if not (
        request.user.role == "ADMIN"
        or request.user.is_superuser
        or invoice.created_by == request.user
    ):
        raise PermissionDenied

    if request.method == "GET":
        Invoice.objects.filter(id=invoice_id).update(is_email_sent=True, status="Sent")
        kwargs = {"domain": request.get_host(), "protocol": request.scheme}
        send_invoice_email.delay(invoice_id, **kwargs)
        return redirect("invoices:invoices_list")


@login_required
@sales_access_required
def invoice_change_status_paid(request, invoice_id):
    invoice = get_object_or_404(Invoice, pk=invoice_id)
    if not (
        request.user.role == "ADMIN"
        or request.user.is_superuser
        or invoice.created_by == request.user
    ):
        raise PermissionDenied

    if request.method == "GET":
        Invoice.objects.filter(id=invoice_id).update(status="Paid")
        kwargs = {"domain": request.get_host(), "protocol": request.scheme}
        # send_invoice_email.delay(invoice_id, **kwargs)
        return redirect("invoices:invoices_list")


@login_required
@sales_access_required
def invoice_change_status_cancelled(request, invoice_id):
    invoice = get_object_or_404(Invoice, pk=invoice_id)
    if not (
        request.user.role == "ADMIN"
        or request.user.is_superuser
        or invoice.created_by == request.user
    ):
        raise PermissionDenied

    if request.method == "GET":
        Invoice.objects.filter(id=invoice_id).update(status="Cancelled")
        kwargs = {"domain": request.get_host(), "protocol": request.scheme}
        send_invoice_email_cancel.delay(invoice_id, **kwargs)
        return redirect("invoices:invoices_list")


@login_required
@sales_access_required
def invoice_download(request, invoice_id):
    invoice = get_object_or_404(
        Invoice.objects.select_related("from_address", "to_address"), pk=invoice_id
    )
    if not (
        request.user.role == "ADMIN"
        or request.user.is_superuser
        or invoice.created_by == request.user
        or request.user in invoice.assigned_to.all()
    ):
        raise PermissionDenied

    if request.method == "GET":
        context = {}
        context["invoice"] = invoice
        html = render_to_string("invoice_download_pdf.html", context=context)
        pdfkit.from_string(html, "out.pdf", options={"encoding": "UTF-8"})
        pdf = open("out.pdf", "rb")
        response = HttpResponse(pdf.read(), content_type="application/pdf")
        response["Content-Disposition"] = "attachment; filename=Invoice.pdf"
        pdf.close()
        os.remove("out.pdf")
        return response


class AddCommentView(LoginRequiredMixin, CreateView):
    model = Comment
    form_class = InvoiceCommentForm
    http_method_names = ["post"]

    def post(self, request, *args, **kwargs):
        self.object = None
        self.invoice = get_object_or_404(Invoice, id=request.POST.get("invoice_id"))
        if (
            request.user == self.invoice.created_by
            or request.user.is_superuser
            or request.user.role == "ADMIN"
        ):
            form = self.get_form()
            if form.is_valid():
                return self.form_valid(form)
            return self.form_invalid(form)

        data = {"error": "You don't have permission to comment for this account."}
        return JsonResponse(data)

    def form_valid(self, form):
        comment = form.save(commit=False)
        comment.commented_by = self.request.user
        comment.invoice = self.invoice
        comment.save()
        comment_id = comment.id
        current_site = get_current_site(self.request)
        send_email_user_mentions.delay(
            comment_id,
            "invoices",
            domain=current_site.domain,
            protocol=self.request.scheme,
        )
        return JsonResponse(
            {
                "comment_id": comment.id,
                "comment": comment.comment,
                "commented_on": comment.commented_on,
                "commented_on_arrow": comment.commented_on_arrow,
                "commented_by": comment.commented_by.email,
            }
        )

    def form_invalid(self, form):
        return JsonResponse({"error": form["comment"].errors})


class UpdateCommentView(LoginRequiredMixin, View):
    http_method_names = ["post"]

    def post(self, request, *args, **kwargs):
        self.comment_obj = get_object_or_404(Comment, id=request.POST.get("commentid"))
        if request.user == self.comment_obj.commented_by:
            form = InvoiceCommentForm(request.POST, instance=self.comment_obj)
            if form.is_valid():
                return self.form_valid(form)

            return self.form_invalid(form)

        data = {"error": "You don't have permission to edit this comment."}
        return JsonResponse(data)

    def form_valid(self, form):
        self.comment_obj.comment = form.cleaned_data.get("comment")
        self.comment_obj.save(update_fields=["comment"])
        comment_id = self.comment_obj.id
        current_site = get_current_site(self.request)
        send_email_user_mentions.delay(
            comment_id,
            "invoices",
            domain=current_site.domain,
            protocol=self.request.scheme,
        )
        return JsonResponse(
            {"comment_id": self.comment_obj.id, "comment": self.comment_obj.comment,}
        )

    def form_invalid(self, form):
        return JsonResponse({"error": form["comment"].errors})


class DeleteCommentView(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        self.object = get_object_or_404(Comment, id=request.POST.get("comment_id"))
        if request.user == self.object.commented_by:
            self.object.delete()
            data = {"cid": request.POST.get("comment_id")}
            return JsonResponse(data)

        data = {"error": "You don't have permission to delete this comment."}
        return JsonResponse(data)


class AddAttachmentView(LoginRequiredMixin, CreateView):
    model = Attachments
    form_class = InvoiceAttachmentForm
    http_method_names = ["post"]

    def post(self, request, *args, **kwargs):
        self.object = None
        self.invoice = get_object_or_404(Invoice, id=request.POST.get("invoice_id"))
        if (
            request.user == self.invoice.created_by
            or request.user.is_superuser
            or request.user.role == "ADMIN"
        ):
            form = self.get_form()
            if form.is_valid():
                return self.form_valid(form)

            return self.form_invalid(form)

        data = {
            "error": "You don't have permission to add attachment \
            for this account."
        }
        return JsonResponse(data)

    def form_valid(self, form):
        attachment = form.save(commit=False)
        attachment.created_by = self.request.user
        attachment.file_name = attachment.attachment.name
        attachment.invoice = self.invoice
        attachment.save()
        return JsonResponse(
            {
                "attachment_id": attachment.id,
                "attachment": attachment.file_name,
                "attachment_url": attachment.attachment.url,
                "download_url": reverse(
                    "common:download_attachment", kwargs={"pk": attachment.id}
                ),
                "attachment_display": attachment.get_file_type_display(),
                "created_on": attachment.created_on,
                "created_on_arrow": attachment.created_on_arrow,
                "created_by": attachment.created_by.email,
                "file_type": attachment.file_type(),
            }
        )

    def form_invalid(self, form):
        return JsonResponse({"error": form["attachment"].errors})


class DeleteAttachmentsView(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        self.object = get_object_or_404(
            Attachments, id=request.POST.get("attachment_id")
        )
        if (
            request.user == self.object.created_by
            or request.user.is_superuser
            or request.user.role == "ADMIN"
        ):
            self.object.delete()
            data = {"acd": request.POST.get("attachment_id")}
            return JsonResponse(data)

        data = {"error": "You don't have permission to delete this attachment."}
        return JsonResponse(data)
